package com.prabhat.webapp.security;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

public class EncryptionUtil {

	private static PBEParameterSpec pbeParamSpec;
	private static Cipher pbeCipher;
	private static SecretKey pbeKey;
	
	/*private static String passPhrase;

	*//**
	 * Sets the Pass Phrase for encryption <br/>
	 * This is set from the spring configuration by picking it from the
	 * properties file
	 * 
	 * @param passPhrase
	 *//*
	public void setPassPhrase(String passPhrase) {
		this.passPhrase = passPhrase;
	}*/

	/**
	 * Initializes the cipher
	 */
	public void init() {
		try {
			byte[] salt = {(byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
				(byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99};

			int count = 20;

			pbeParamSpec = new PBEParameterSpec(salt, count);

			PBEKeySpec pbeKeySpec = new PBEKeySpec("wavecrest".toCharArray());
			//SecretKeyFactory keyFac = SecretKeyFactory.getInstance(GenericEnum.PBEWithMD5AndDES.value());
			SecretKeyFactory keyFac = SecretKeyFactory.getInstance(CoreEnum.PBEWithMD5AndTripleDES.value());
			pbeKey = keyFac.generateSecret(pbeKeySpec);

			pbeCipher = Cipher.getInstance(keyFac.getAlgorithm());

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Encrypts the given string using 3 DES and MD5
	 * @param data String to encrypt
	 * @return Encrypted string
	 * @throws InvalidKeyException
	 * @throws InvalidAlgorithmParameterException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	public synchronized static String md5AndDesEncrypt(String data) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
		pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
		byte[] cleartext = data.getBytes();
		byte[] ciphertext = pbeCipher.doFinal(cleartext);
		String encrypted = byteToHex(ciphertext);

		return encrypted;
	}

	/**
	 * Decrypts the encrypted string using 3 DES and MD5
	 * @param encrypted encrypted string
	 * @return decrypted value
	 * @throws InvalidKeyException
	 * @throws InvalidAlgorithmParameterException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	public synchronized static String md5AndDesDecrypt(String encrypted) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {

		try {
			byte[] salt = {(byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
				(byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99};

			int count = 20;

			pbeParamSpec = new PBEParameterSpec(salt, count);

			PBEKeySpec pbeKeySpec = new PBEKeySpec("wavecrest".toCharArray());
			//SecretKeyFactory keyFac = SecretKeyFactory.getInstance(GenericEnum.PBEWithMD5AndDES.value());
			SecretKeyFactory keyFac = SecretKeyFactory.getInstance(CoreEnum.PBEWithMD5AndTripleDES.value());
			pbeKey = keyFac.generateSecret(pbeKeySpec);

			pbeCipher = Cipher.getInstance(keyFac.getAlgorithm());

		} catch (Exception e) {
			e.printStackTrace();
		}

		
		byte[] encbytes = hexToByte(encrypted);
		pbeCipher.init(javax.crypto.Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);	
		byte[] decbytes = pbeCipher.doFinal(encbytes);
		return new String(decbytes);
	}

	/**
	 * Converts Hex decimal byte array to String
	 * @param hash
	 * @return
	 */
	private static String byteToHex(byte[] hash) {
		StringBuffer buf = new StringBuffer(hash.length * 2);
		int i;
		for (i = 0; i < hash.length; i++) {
			if (((int) hash[i] & 0xff) < 0x10) {
				buf.append("0");
			}
			buf.append(Integer.toHexString((int) hash[i] & 0xff));
		}
		return (buf.toString());
	}

	/**
	 * Converts String to Hex decimal byte array
	 * @param hexStr
	 * @return
	 */
	private static byte[] hexToByte(String hexStr) {
		if (hexStr == null) {
			return (null);
		}
		byte[] barray = new byte[hexStr.length() / 2];
		int j = 0;
		for (int i = 0; i < hexStr.length(); i += 2) {
			String tempStr = hexStr.substring(i, i + 2);
			barray[j] = Integer.valueOf(tempStr, 16).byteValue();
			j++;
		}
		return (barray);
	}

	/**
	 * Returns MD5 sum for the given string
	 * 
	 * @param str
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	public static String shaEncrypt(String str) {
		try {
		MessageDigest md;
		md = MessageDigest.getInstance("SHA1");
		byte[] sha1hash = new byte[40];
		md.update(str.getBytes("iso-8859-1"), 0, str.length());
		sha1hash = md.digest();
		return byteArrayToBase64(sha1hash);
		}catch (Exception e) {
			throw (new RuntimeException("no such algorithm"));
		}
	}

	private static String convertToHex(byte[] data) {
		StringBuffer buf = new StringBuffer();
		for (int i = 0; i < data.length; i++) {
			int halfbyte = (data[i] >>> 4) & 0x0F;
			int two_halfs = 0;
			do {
				if ((0 <= halfbyte) && (halfbyte <= 9))
					buf.append((char) ('0' + halfbyte));
				else
					buf.append((char) ('a' + (halfbyte - 10)));
				halfbyte = data[i] & 0x0F;
			} while (two_halfs++ < 1);
		}
		return buf.toString();
	}

	public static void main(String[] args) {
		try {
			EncryptionUtil encryptionUtil = new EncryptionUtil();
			encryptionUtil.init();
			//System.out.println(EncryptionUtil.md5AndDesDecrypt("3721c6cf991681575e06fb4ab9f5340918ade63233b857f4"));
			System.out.println(EncryptionUtil.md5AndDesEncrypt("Prabhat Kumar Kashyap"));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
	}
	
	public static String byteArrayToBase64(byte[] a) {
		int aLen = a.length;
		int numFullGroups = aLen / 3;
		int numBytesInPartialGroup = aLen - 3 * numFullGroups;
		int resultLen = 4 * ((aLen + 2) / 3);
		StringBuffer result = new StringBuffer(resultLen);
		char[] intToAlpha = intToBase64;
		// Translate all full groups from byte array elements to Base64
		int inCursor = 0;
		for (int i = 0; i < numFullGroups; i++) {
		int byte0 = a[inCursor++] & 0xff;
		int byte1 = a[inCursor++] & 0xff;
		int byte2 = a[inCursor++] & 0xff;
		result.append(intToAlpha[byte0 >> 2]);
		result.append(intToAlpha[(byte0 << 4) & 0x3f | (byte1 >> 4)]);
		result.append(intToAlpha[(byte1 << 2) & 0x3f | (byte2 >> 6)]);
		result.append(intToAlpha[byte2 & 0x3f]);
		}
		// Translate partial group if present
		if (numBytesInPartialGroup != 0) {
		int byte0 = a[inCursor++] & 0xff;
		result.append(intToAlpha[byte0 >> 2]);
		if (numBytesInPartialGroup == 1) {
		result.append(intToAlpha[(byte0 << 4) & 0x3f]);
		result.append("==");
		} else {
		// assert numBytesInPartialGroup == 2;
		int byte1 = a[inCursor++] & 0xff;
		result.append(intToAlpha[(byte0 << 4) & 0x3f | (byte1 >> 4)]);
		result.append(intToAlpha[(byte1 << 2) & 0x3f]);
		result.append('=');
		}
		}
		// assert inCursor == a.length;
		// assert result.length() == resultLen;
		return result.toString();
		}
		 
		/**
		* This array is a lookup table that translates 6-bit positive integer
		* index values into their "Base64 Alphabet" equivalents as specified
		* in Table 1 of RFC 2045.
		*/
	
		private static final char intToBase64[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
		'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
		'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
		'4', '5', '6', '7', '8', '9', '+', '/'};


}

/*CoreEnum class ........................................................*/
 enum CoreEnum {
	MD5("MD5", "MD5"), PBEWithMD5AndDES("PBEWithMD5AndDES", "PBEWithMD5AndDES"), PBEWithMD5AndTripleDES("PBEWithMD5AndTripleDES","PBEWithMD5AndTripleDES");

	CoreEnum(String value, String stringValue) {
		this.value = value;
		this.stringValue = stringValue;
	}

	private final String value;
	private final String stringValue;

	public String value() {
		return this.value;
	}

	public String toString() {
		return stringValue;
	}
}

